package source.events.action;

import source.model.*;
import source.model.skill.*;
import source.model.type.ActionResult;
import source.model.type.GameObjectType;
import source.view.MainScreen;
import source.model.utility.PositionUtility;

/**
 * Observe class is responsible making the current unit observe.  If the
 * current selection is not a unit, the Action fails. If the current unit
 * cannot observe, the Action is impossible. If the current unit cannot see
 * as far as the square requested to observe, it is impossible.
 * 
 * 
 * @author Joe/Tristan
 *
 */

public class Observe implements Action{

	private Unit unit;
	private int pid;
	private Position position;
	private int unitTypeThresh = 40;
	private int unitHealthThresh = 60;
	private int unitMaxHealthThresh = 50;
	private int unitArmorThresh = 80;
	private int unitDamageThresh = 70;
	
	public Observe(int pid, Position position)
	{
		this.pid = pid;
		this.position = position;
	}
	public ActionResult execute( DataHandler dh ) {
		
		GameObject go = dh.getCurrentSelection();

		if(go.objectType() != GameObjectType.UNIT)
		{
			MainScreen.writeToConsole("Current selection is not an unit");
			return ActionResult.IMPOSSIBLE;
		}
		
		this.unit = (Unit)go;
		if(unit.getMissionFlag())
		{
			MainScreen.writeToConsole("Current selection is on a mission");
			return ActionResult.IMPOSSIBLE;
		}
		Skill theSkill = unit.getSkill("observe");
		
		if (theSkill == null){
			MainScreen.writeToConsole(unit.getName() + "s cannot Observe");
			return ActionResult.IMPOSSIBLE;
		}
		SkillObserve obs = (SkillObserve)theSkill;

		Position uPos = dh.positionQuery(unit.objectID());
		if (!(PositionUtility.inRange(uPos, position, unit.getView()))){
			MainScreen.writeToConsole("Observe: " + unit.getName() + " cannot see that far!\n");
			return ActionResult.IMPOSSIBLE;
		}
		
		GameObject[] contains = dh.positionQuery(position, GameObjectType.UNIT);
		Unit[] enemyUnits = new Unit[contains.length];
		for (int i = 0; i < contains.length; i++){
			enemyUnits[i] = (Unit)contains[i];
		}
		
		int obsLevel = obs.obsLevel();
		
		
		// Prints all the crap
		for (Unit u:enemyUnits){
			
			int uArmor = u.armor() + offset(unitArmorThresh, obsLevel);
			uArmor = (uArmor >= 0 ? uArmor : 0);
			MainScreen.writeToConsole("It has " + uArmor + " armor.\n");
			
			int dDamage = u.dDamage() + offset(unitDamageThresh, obsLevel);
			int oDamage = u.oDamage() + offset(unitDamageThresh, obsLevel);
			dDamage = (dDamage >= 0 ? dDamage : 0);
			oDamage = (oDamage >= 0 ? oDamage : 0);
			MainScreen.writeToConsole("It has " + dDamage + " defense power.");
			MainScreen.writeToConsole("It has " + oDamage + " attack power.");

			int maxHealth = u.maxHealth() + offset(unitMaxHealthThresh, obsLevel);
			int curHealth = u.currentHealth() + offset(unitHealthThresh, obsLevel);
			maxHealth = (maxHealth >= 1 ? maxHealth : 1);
			curHealth = (curHealth > maxHealth ? maxHealth : curHealth);
			String uName = (chanceSkillValue(obsLevel) - 2 >= unitTypeThresh ? u.getName() : "unit");
			MainScreen.writeToConsole("There is a " + uName + " with health " + curHealth + "/" +  maxHealth + ".");
		}
		MainScreen.writeToConsole("At Position (" + position.getX() + "," + position.getY() + ") there exists:\n");
		
		return ActionResult.SUCCESS;
	}
	
	/**
	 * This calculates the a semi-random chance number, used to calculate
	 * probability that a Unit can observe a given attribute about enemy
	 * Units on the square. The minimum thresh hold is the observe skill * 10
	 * 
	 * @param obsLevel
	 * @return Probability of success.
	 */
	private double chanceSkillValue(int obsLevel){
		obsLevel = (obsLevel > 10 ? 100 : obsLevel * 10);
		int min = obsLevel;
		int range = 100 - obsLevel;
		double chance = Math.random() * range + min;
		return chance;
	}
	
	/**
	 * 
	 * @param floor
	 * @param obsLevel
	 * @return
	 */
	private int offset(int floor, int obsLevel){
		double myRoll = chanceSkillValue(obsLevel);
		double opRoll = Math.random() * 108;
		double offset = Math.random() * 2 *(opRoll - myRoll) - ((opRoll - myRoll)/2) ;
		offset /= 8;
		return (myRoll >= opRoll ? 0 : (int)offset);
	}
}
